/************************************************************************

								Heroes IV
					  Copyright 2002, The 3DO Company

	------------------------------------------------------------------
  						      adv_artifact_pile.cpp

	$Header: $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "adv_artifact_pile.h"

#include "adaptor_handler.h"
#include "adv_object_type.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "army.h"
#include "dialog_booty.h"
#include "function_handler.h"
#include "handler.h"
#include "object_registration.h"
#include "options.h"
#include "player.h"
#include "report_combat_results.h"
#include "simple_dialog.h"

namespace
{
	int const k_current_save_version = 0;

	t_object_registration< t_adv_artifact_pile > k_registration( k_adv_object_artifact_pile );
};


// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
t_adv_artifact_pile::t_adv_artifact_pile( std::string const&  model_name )
	: t_stationary_adventure_object( model_name )
{
	
}

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
t_adv_artifact_pile::t_adv_artifact_pile( bool is_ocean )
	: t_stationary_adventure_object( is_ocean ? "Internal.Artifact Pile Ocean" : "Internal.Artifact Pile Land" )
{

}

// ---------------------------------------------------------------------------
// Give treasure to triggering army
// ---------------------------------------------------------------------------
void t_adv_artifact_pile::activate_trigger( t_army* army, t_adv_map_point const& point, 
											t_direction direction, t_adventure_frame* frame )
{
	if ( army->get_owner() && !army->get_owner()->is_computer() )
	{
		// Just in case...
		if ( m_artifacts.empty() )
		{
			ok_dialog( get_text( "empty"), get_dialog_bottom( point ), get_name() );
		}
		else
		{
			t_counted_ptr< t_dialog_booty > booty_dialog = new t_dialog_booty( frame, *army,
																			   m_artifacts, 
																			   get_name(),
																			   get_text( "initial" ) );
			booty_dialog->run_modal();
		}
	}
	else
	{
		auto_distribute_booty( *army, m_artifacts );
	}

	destroy();
}

// ---------------------------------------------------------------------------
// The given player or players are adding artifacts to our pile
// ---------------------------------------------------------------------------
void t_adv_artifact_pile::add_artifacts( t_artifact_list const & artifacts, t_player * player1, t_player * player2 )
{
	m_artifacts.insert( m_artifacts.begin(), artifacts.begin(), artifacts.end() );

	add_player( player1 );
	add_player( player2 );
}


// ---------------------------------------------------------------------------
// Add to the list of players who can always see this artifact pile
// ---------------------------------------------------------------------------
void t_adv_artifact_pile::add_player( t_player * player )
{
	if ( player )
	{
		int team = player->get_team();

		if ( m_teams.size() <= team )
		{
			m_teams.resize( team + 1, false );
		}

		m_teams[team] = true;
	}
}

// ---------------------------------------------------------------------------
// Current save version
// ---------------------------------------------------------------------------
int t_adv_artifact_pile::get_version() const
{
	return k_current_save_version;
}

// ---------------------------------------------------------------------------
// Don't delete from deletion markers, since map maker didn't know about us
// ---------------------------------------------------------------------------
bool t_adv_artifact_pile::is_deleted_by_deletion_marker() const
{
	return false;
}

// ---------------------------------------------------------------------------
// Read from save file
// ---------------------------------------------------------------------------
bool t_adv_artifact_pile::read( std::streambuf&						stream, 
								t_qualified_adv_object_type const&	type,
								int									version )
{
	if ( version < 0 || version > k_current_save_version )
		return false;

	m_artifacts.clear();

	int count = get< t_uint16 >( stream );

	while ( count )
	{
		m_artifacts.push_back( t_artifact() );
		if (!m_artifacts.back().read( stream ))
			return false;

		count--;
	}

	if ( !m_teams.read( stream ) )
		return false;

	return true;
}

// ---------------------------------------------------------------------------
// Player whose artifacts are in pile can always see it, so that they know
// where the artifacts went. Otherwise, don't tell other players that the
// first player lost a battle
// ---------------------------------------------------------------------------
bool t_adv_artifact_pile::is_visible_to( int team_num ) const
{
	if (!get_enable_shroud())
		return true;

	t_adventure_tile const& tile = (*get_map())[get_position()];

	if (tile.get_visibility( team_num ) == k_tile_visible)
		return true;

	if ( team_num < 0 )
		return true;

	if ( m_teams.size() <= team_num )
		return false;

	return (m_teams[team_num]==0 ? false : true);
}

bool t_adv_artifact_pile::can_be_hidden() const
{
	return true;
}

// ---------------------------------------------------------------------------
// Write to save file
// ---------------------------------------------------------------------------
bool t_adv_artifact_pile::write( std::streambuf& stream ) const
{
	put< t_uint16 >( stream, m_artifacts.size() );

	t_artifact_list::const_iterator it;

	for ( it = m_artifacts.begin(); it != m_artifacts.end(); it++ )
	{
		if ( !it->write( stream ) )
			return false;
	}

	if ( !m_teams.write( stream ) )
		return false;

	return true;
}

// -----------------------------------------------------------------------
// Is this a good place to put a pile of artifacts? If there already is
// a pile here, return it in pile
// Static function
// -----------------------------------------------------------------------
bool t_adv_artifact_pile::is_good_creation_point( t_adv_map_point point, t_adv_artifact_pile_ptr & pile, t_adventure_map * map )
{
	t_adventure_tile const & tile = (*map)[point];

	int count = tile.get_intersecting_object_count();
	int i;

	for ( i = 0; i < count; i++ )
	{
		t_adventure_map_object_id id = tile.get_intersecting_object_id( i );

		t_adventure_object & object = map->get_adv_object( id );

		// Compare "on bridge" flag
		if ( object.get_position() == point )
		{
			t_adv_artifact_pile * object_as_pile = dynamic_cast< t_adv_artifact_pile * >( &object );

			if ( object_as_pile )
			{
				pile = object_as_pile;
				return true;
			}
		}
	}

	return !tile.is_blocked( point.on_bridge ); // Does this have PERMENANTLY blocking objects?
}



namespace
{
	// Data for object_pile_scanner()
	struct t_object_pile_scanner_data 
	{
		t_adv_artifact_pile_ptr				 existing_pile;
		t_adventure_map						*map;
		t_adv_map_point						 best_point;
		bool								 best_point_is_decent;
		bool								 best_point_is_unblocked;
		t_creature_array const *			 sample_army;
	};
}

// Scanner function for use with t_adventure_object::scan_adjacent_spaces
// Looks for a good place to put an artifact pile
static void object_pile_scanner( t_adv_map_point const & point, bool & stop, t_object_pile_scanner_data & data )
{
	// Don't place under ramps
	if ( !point.on_bridge && data.map->get_adv_tile( point ).has_ramp() )
		return;

	if ( t_adv_artifact_pile::is_good_creation_point( point, data.existing_pile, data.map ) )
	{
		if ( data.existing_pile != NULL )
		{
			// As good as it gets
			data.best_point = point;
			stop = true;
			return;
		}

		if ( data.best_point_is_unblocked )
			return;

		// Is this point blocked at all?
		if ( data.map->is_open( point, *data.sample_army ) )
		{
			data.best_point_is_decent = true;
			data.best_point_is_unblocked = true;
			data.best_point = point;
		}

		if ( data.best_point_is_decent )
			return;

		// Decent, but keep looking for better.
		data.best_point = point;
		data.best_point_is_decent = true;
	}
	else
	{
		if ( !data.best_point_is_decent )
		{
			data.best_point = point;
		}
	}
}


// -----------------------------------------------------------------------
// Scan all points near an adventure object looking for a good place to 
// put an artifact pile
// Static function
// -----------------------------------------------------------------------
bool t_adv_artifact_pile::scan_for_good_creation_point( t_adventure_object const & object, t_creature_array const & sample_army, t_adv_map_point & point, t_adv_artifact_pile_ptr & pile )
{
	assert( object.is_on_map() );

	// Scan nearby points for good location
	t_object_pile_scanner_data data;
	data.best_point_is_decent = false;
	data.best_point_is_unblocked = false;
	data.best_point = object.get_position();
	data.existing_pile = pile;
	data.map = object.get_map();
	data.sample_army = &sample_army;

	t_adv_map_point result;

	t_handler_3< t_adv_map_point const &, bool &, t_object_pile_scanner_data & > sub_handler_1 =
		function_3_handler( object_pile_scanner );

	t_handler_2< t_adv_map_point const &, bool & > sub_handler_2 = 
		add_3rd_argument< t_object_pile_scanner_data & >( sub_handler_1, data );

	// Can't get this to compile.... I think we may be overstraining the compiler's
	// template handling at this point
	// t_adventure_object::t_scan_adjacent_space_handler scanner = discard_2nd_argument< t_adv_map_point const &, t_adv_map_point const &, bool & >( sub_handler_2 );
	t_adventure_object::t_scan_adjacent_space_handler scanner = new t_discard_2nd_handler_3< t_adv_map_point const &, t_adv_map_point const &, bool & >( sub_handler_2 );

	object.scan_adjacent_spaces( scanner, result );

	pile = data.existing_pile;
	point = data.best_point;

	return data.best_point_is_decent;
}


// -----------------------------------------------------------------------
// Take all artifacts from an army & put them in an artifact pile
// Pile will be directly under the array; if the array doesn't have
// a location, the function will do nothing
// Static function
// -----------------------------------------------------------------------
void t_adv_artifact_pile::pull_artifacts_from_array( t_creature_array & array, t_player * visible_to_player )
{
	t_adventure_map * map = array.get_map();

	if ( !map )
		return;

	t_adv_map_point point;

	if ( !array.get_virtual_position( point ) )
		return;

	int i;

	t_artifact_list leftovers;

	t_army empty_army; // To ensure that redistribute_artifacts can't move artifacts to other stacks

	for ( i = 0; i < t_creature_array::k_size; i++ )
	{
		t_creature_stack & stack = array[ i ];

		stack.redistribute_artifacts( empty_army, leftovers );
	}

	if ( !leftovers.empty() )
	{
		// Find or create booty pile
		t_counted_ptr< t_adv_artifact_pile > pile;

		// Look for existing pile
		is_good_creation_point( point, pile, map );

		if ( pile == NULL )
		{
			pile = new t_adv_artifact_pile( map->is_ocean( point ) );

			pile->place( *map, point );
		}

		pile->add_artifacts( leftovers, visible_to_player, NULL );
	}
}
